/**
 * 
 */
package org.mspring.mlog.web.security;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.apache.log4j.Logger;
import org.mspring.mlog.common.Keys;
import org.mspring.mlog.entity.security.User;
import org.mspring.platform.core.ContextManager;
import org.mspring.platform.utils.CookieUtils;
import org.mspring.platform.utils.RequestUtils;
import org.mspring.platform.utils.StringUtils;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.web.authentication.WebAuthenticationDetails;
import org.springframework.security.web.context.HttpSessionSecurityContextRepository;

/**
 * @author Administrator
 * @since Gao Youbo
 * @description
 * @TODO
 */
public class SecurityUtils {
    private static final Logger log = Logger.getLogger(SecurityUtils.class);

    /**
     * 获取当前登录用户的UserDetails
     * 
     * @return
     */
    public static UserDetailsImpl getUserDetailsImpl() {
        Authentication authentication = getAuthentication();
        if (authentication != null) {
            Object principal = authentication.getPrincipal();
            if (principal != null && principal instanceof UserDetailsImpl) {
                return (UserDetailsImpl) principal;
            }
        }
        return null;
    }

    /**
     * 获取当前登录的User对象
     * 
     * @return
     */
    public static User getCurrentUser() {
        UserDetailsImpl userDetailsImpl = getUserDetailsImpl();
        if (userDetailsImpl == null) {
            log.debug("can't get logined user.");
            return null;
        }
        return userDetailsImpl.getUserEntity();
    }

    /**
     * 获取当前登录用户
     * 
     * @param request
     * @return
     */
    public static User getCurrentUser(HttpServletRequest request) {
        return getCurrentUser(request.getSession());
    }

    /**
     * 获取当前登录用户
     * 
     * @param session
     * @return
     */
    public static User getCurrentUser(HttpSession session) {
        Object obj = session.getAttribute(Keys.CURRENT_USER);
        if (obj != null && obj instanceof User) {
            return (User) obj;
        } else {
            User user = getCurrentUser();
            if (user != null) {
                session.setAttribute(Keys.CURRENT_USER, user);
                return user;
            }
        }
        return null;
    }

    /**
     * 判断是否有后台访问权限
     * 
     * @param request
     * @return
     */
    public static boolean hasAdminAccessToken(HttpServletRequest request) {
        User currentUser = SecurityUtils.getCurrentUser(request);
        if (currentUser == null) {
            return false;
        }
        Object token = request.getSession().getAttribute(Keys.ADMIN_ACCESS_TOKEN);
        if (currentUser.getId().equals(token)) {
            return true;
        }
        currentUser = autoLoginAdmin(request);
        if (currentUser == null) {
            return false;
        }
        token = request.getSession().getAttribute(Keys.ADMIN_ACCESS_TOKEN);
        if (!currentUser.getId().equals(token)) {
            return false;
        }
        return true;
    }

    /**
     * 设置用户具备Admin的访问权限
     * 
     * @param request
     * @param userId
     */
    public static void setAdminAccessToken(HttpServletRequest request, Long userId) {
        request.getSession().setAttribute(Keys.ADMIN_ACCESS_TOKEN, userId);
    }

    /**
     * Admin自动登录
     * 
     * @param request
     */
    public static User autoLoginAdmin(HttpServletRequest request) {
        String admin_remember = CookieUtils.getCookie(request, "admin_remember");
        if ("true".equals(admin_remember)) {
            String admin_name = CookieUtils.getCookie(request, "admin_name");
            String admin_password = CookieUtils.getCookie(request, "admin_password");
            if (StringUtils.isNotBlank(admin_name) && StringUtils.isNotBlank(admin_password)) {
                UserDetailServiceImpl userDetailsService = ContextManager.getApplicationContext().getBean(UserDetailServiceImpl.class);
                UserDetailsImpl userDetails = (UserDetailsImpl) userDetailsService.loadUserByUsername(admin_name);
                if (userDetails != null && userDetails.getUserEntity() != null && admin_password.equals(userDetails.getPassword())) {
                    reloadUserDetails(userDetails, request);
                    setAdminAccessToken(request, userDetails.getUserEntity().getId());
                    return userDetails.getUserEntity();
                }
            }
        }
        return null;
    }

    /**
     * 记住Admin登录状态
     * 
     * @param request
     */
    public static void rememberAdmin(User user, HttpServletRequest request, HttpServletResponse response) {
        if (user == null) {
            return;
        }
        Boolean rememberMe = RequestUtils.getRequestParameterAsBoolean(request, "rememberMe");
        if (rememberMe) {
            CookieUtils.setCookie(response, "admin_name", user.getName(), 365);
            CookieUtils.setCookie(response, "admin_password", user.getPassword(), 365);
            CookieUtils.setCookie(response, "admin_remember", "true", 365);
        }
    }

    /**
     * 从新加载所有资源-角色管理
     */
    public static void reloadResourceDefine() {
        SecurityMetadataSource securityMetadataSource = ContextManager.getApplicationContext().getBean(SecurityMetadataSource.class);
        if (securityMetadataSource != null) {
            securityMetadataSource.reloadResourceDefine();
        }
    }

    /**
     * 取得当前用户登录IP, 如果当前用户未登录则返回空字符串.
     */
    public static String getCurrentUserIp() {
        Authentication authentication = getAuthentication();
        if (authentication != null) {
            Object details = authentication.getDetails();
            if (details instanceof WebAuthenticationDetails) {
                WebAuthenticationDetails webDetails = (WebAuthenticationDetails) details;
                return webDetails.getRemoteAddress();
            }
        }
        return "";
    }

    public static void logout(HttpServletRequest request) {
        request.getSession().setAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY, null);
        request.getSession().setAttribute(Keys.CURRENT_USER, null);
        request.getSession().invalidate();
    }

    /**
     * 重新加载、登录
     * 
     * @param userDetails
     * @param request
     */
    public static void reloadUserDetails(UserDetails userDetails, HttpServletRequest request) {
        Authentication authentication = new UsernamePasswordAuthenticationToken(userDetails, userDetails.getPassword(), userDetails.getAuthorities());
        SecurityContext securityContext = SecurityContextHolder.getContext();
        securityContext.setAuthentication(authentication);

        request.getSession().setAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY, securityContext);
        User currentUser = ((UserDetailsImpl) userDetails).getUserEntity();
        request.getSession().setAttribute(Keys.CURRENT_USER, currentUser);
    }

    /**
     * 重新加载、登录
     * 
     * @param username
     * @param request
     */
    public static void reloadUserDetails(String username, HttpServletRequest request) {
        UserDetailServiceImpl userDetailsService = ContextManager.getApplicationContext().getBean(UserDetailServiceImpl.class);
        UserDetails userDetails = userDetailsService.loadUserByUsername(username);
        reloadUserDetails(userDetails, request);
    }

    /**
     * 取得Authentication, 如当前SecurityContext为空时返回null.
     * 
     * @return
     */
    private static Authentication getAuthentication() {
        SecurityContext context = SecurityContextHolder.getContext();
        if (context != null) {
            return context.getAuthentication();
        }
        return null;
    }
}
